/**
 * @file FileList.cpp
 * @author Piotr Jasiun
 *
 * Implementacje klas odpowiedzialnych za odbługę pliku konfiguracyjnego zawierającego informacje o położeniu żądanych plików oraz klasy udostępniającej żądany fragment pliku.
 */

#include "FileList.h"
#include "Log.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdio.h>
#include <sys/stat.h>

namespace tftp_server
{
//klasa File

	int File::getSize()
	{
		if (isNull())
		{
			Loger::getInstance().send(Log("File: Próba odczytania rozmiaru nieznalezionego pliku: '"+fileName+"'",2));
			return 0;
		}

		FILE * file = fopen (getRealFileName().c_str() , "rb");

		if (file == NULL)
		{
			Loger::getInstance().send(Log("File: Próba odczytania rozmiaru nieistniejącego pliku: "+fileName,2));
			return 0;
		}

		fseek(file, 0, SEEK_END);
		int fileSize = ftell(file);
		rewind(file);
		fclose(file);

		return fileSize;
	};

	bool File::isExist()
	{
		FILE * file = fopen (getRealFileName().c_str() , "rb");

		if (file == NULL)
				return false;

		fclose(file);

		return true;
	};

	bool File::isAccessible ()
	{
		struct stat statbuf;

		if (stat(getRealFileName().c_str(),&statbuf) < 0)
			return false;

		if (statbuf.st_mode & (S_IREAD >> 6) == 0)
			return false;

		return true;
	};

//klasa BinFile

	int BinFile::getFragment(int from, int size, char * content)
	{
		int FileSize = getSize();
		if (from > FileSize)
			return 0;

		FILE * file = fopen (getRealFileName().c_str() , "rb");

		if (file == NULL)
				return 0;

		fseek(file, from, 0);

		int i=0;

		while ((i < size) && (from + i < FileSize))
		{
			content[i]=fgetc(file);
			++i;
		}

		fclose(file);

		return i;

	}

//klasa AsciiFile

	AsciiFile::AsciiFile(string _fileName)
	{
		File::fileName=_fileName;
		int fileSize = getSize();
		transformatedFileSize = 0;

		if (fileSize == 0)
		{
			fileContents = new char [1];
			return;
		}

		fileContents = new char [fileSize*2];

		FILE * file = fopen (getRealFileName().c_str() , "rb");

		if (file == NULL)
			return;


		int form = 0;
		int to = 0;
		while (form < fileSize)
		{
			char c=getc(file);

			switch (c)
			{
			case '\n':
				fileContents[to]='\r';
				++to;
				fileContents[to]='\n';
				break;
			case '\r':
				fileContents[to]='\r';
				++to;
				fileContents[to]='\0';
				break;
			default:
				fileContents[to]=c;
			}

			++to;
			++form;
		}

		transformatedFileSize = to;

		fclose(file);
	}

	int AsciiFile::getFragment(int from, int size, char * content)
	{
		if (from > transformatedFileSize)
			return 0;

		int i=0;

		while ((i < size) && (from + i < transformatedFileSize))
		{
			content[i]=fileContents[from + i];
			++i;
		}

		return i;
	}

//klasa FileList

	bool FileList::setConfigFile(std::string FileName)
	{
		ifstream fin;
		fin.open(FileName.c_str());
		if (!fin)
		{
			Loger::getInstance().send(Log("Plik konfiguracyjny: Blad otwarcia pliku konfiguracyjnego!",2));
			return false;
		}

		IPlist.clear();
		MAClist.clear();

		string line;
		int lineNum = 1;

		while (getline(fin,line))
		{
			if ((line.c_str()[0] != '/') || (line.c_str()[1] != '/'))
			{
				stringstream sin(line);
				string type;
				sin >> type;
				if (type == "IP")
				{
					FileIP newRecord;
					bool correct = true;

					string ip;
					sin >> ip;
					newRecord.FileIP = IP(ip);

					sin >> newRecord.fileName;
					sin >> newRecord.realFile;

					if (newRecord.realFile == "")
					{
						correct = false;
						Loger::getInstance().send(Log("Plik konfiguracyjny: niepoprawna ilość elemantów w pliku " + FileName + ", w linii " + intToStr(lineNum),1));
					}

					if (correct)
					{
						if ((newRecord.fileName == "*") || (ip == "*"))
							IPlist.push_back(newRecord);
						else
							IPlist.push_front(newRecord);
					}
				}
				else if (type == "MAC")
				{
					FileMAC newRecord;
					bool correct = true;

					string mac;
					sin >> mac;

					newRecord.FileMAC = MAC(mac);

					sin >> newRecord.fileName;
					sin >> newRecord.realFile;

					if (newRecord.realFile == "")
					{
						correct = false;
						Loger::getInstance().send(Log("Plik konfiguracyjny: niepoprawna ilość elemantów w pliku " + FileName + ", w linii " + intToStr(lineNum) ,1));
					}

					if (correct)
					{
						if ((newRecord.fileName == "*") || (mac == "*"))
							MAClist.push_back(newRecord);
						else
							MAClist.push_front(newRecord);
					}
				}
				else if (type == "")
					Loger::getInstance().send(Log("Plik konfiguracyjny: '"+FileName+"': Pusta  linia" ,1));
				else
					Loger::getInstance().send(Log("Plik konfiguracyjny: '"+FileName+"': Nieznany typ danych ("+type+") w linii " + intToStr(lineNum),3));
				line = "";

			}
			++lineNum;
		}


		fin.close();
		return true;
	};

	void FileList::show()
	{
		std::list<FileMAC>::iterator iM;

		cout << "Konfiguracja dostępnych plików:";

		for( iM = MAClist.begin(); iM != MAClist.end(); ++iM )
			 cout << "MAC" << "\t" << iM->FileMAC.mac << "\t" << iM->fileName << "\t" << iM->realFile << "\n";

		std::list<FileIP>::iterator iP;

		for( iP = IPlist.begin(); iP != IPlist.end(); ++iP )
			 cout << "IP" << "\t" << iP->FileIP << "\t" << iP->fileName << "\t" << iP->realFile << "\n";
	};

	File * FileList::selectType(std::string fileName, char type)
	{
		if (type == 'a')
		{
			File * ret = new AsciiFile(fileName);
			return ret;
		}
		else if (type == 'b')
		{
			File * ret = new BinFile(fileName);
			return ret;
		}

		string stype(&type);
		Loger::getInstance().send(Log("FileList: Próba odczytu pliku w nieznanym typie: "+stype+"!",1));

		File * ret = new BinFile("");
		return ret;
	}

	File* FileList::getFile(IP ip, MAC mac, std::string fileName, char type)
	{
		std::list<FileMAC>::iterator iM;

		for( iM = MAClist.begin(); iM != MAClist.end(); ++iM )
		{
			 if (iM->FileMAC.mac == mac.mac && iM->fileName == fileName)
					 return selectType(iM->realFile,type);
			 if (iM->FileMAC.mac == mac.mac && iM->fileName == "*")
					 return selectType(iM->realFile,type);
			 if (iM->FileMAC.mac == "*" && iM->fileName == fileName)
					 return selectType(iM->realFile,type);
		}

		std::list<FileIP>::iterator iP;

		for( iP = IPlist.begin(); iP != IPlist.end(); ++iP )
		{
			 if (iP->FileIP == ip && iP->fileName == fileName)
				 return selectType(iP->realFile,type);
			 if (iP->FileIP == ip && iP->fileName == "*")
				 return selectType(iP->realFile,type);
			 if (iP->FileIP == IP("*") && iP->fileName == fileName)
				 return selectType(iP->realFile,type);
		}

		return selectType("",'b');
	}
}
